package com.yhd.gmall.cart.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yhd.gmall.cart.mapper.CartInfoMapper;
import com.yhd.gmall.common.constant.RedisConst;
import com.yhd.gmall.model.cart.CartInfo;
import com.yhd.gmall.model.product.SkuInfo;
import com.yhd.gmall.product.client.ProductFeignClient;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author yhd
 * @createtime 2020/9/26 12:01
 */
@Service
public class CartInfoService {

    @Autowired
    private CartInfoMapper cartInfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductFeignClient productFeignClient;

    /**
     * 添加购物车
     * 商品id，用户id，商品数量
     */
    public void addToCart(Long skuId, String userId, Integer skuNum) {
        //获取购物车的key
        String cartKey = getCartKey(userId);
        //获取数据库对象
        QueryWrapper<CartInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("sku_id", skuId).eq("user_id", userId);
        CartInfo cartInfo = cartInfoMapper.selectOne(wrapper);
        //数据库中有数据
        if (cartInfo != null) {
            cartInfo.setSkuNum(cartInfo.getSkuNum() + skuNum);
            BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);
            if (null != skuPrice)
                cartInfo.setSkuPrice(skuPrice);
            cartInfoMapper.updateById(cartInfo);
        } else {
            //如果缓存中没有这件商品
            CartInfo cart = new CartInfo();
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            cart.setSkuPrice(skuInfo.getPrice());
            cart.setCartPrice(skuInfo.getPrice());
            cart.setSkuNum(skuNum);
            cart.setSkuId(skuId);
            cart.setUserId(userId);
            cart.setImgUrl(skuInfo.getSkuDefaultImg());
            cart.setSkuName(skuInfo.getSkuName());
            cartInfoMapper.insert(cart);
            cartInfo = cart;
        }
        //更新缓存
        updateCache(cartKey, skuId, cartInfo);
    }

    /**
     * 更新缓存
     */
    @Async
    public void updateCache(String cartKey, Long skuId, CartInfo cartInfo) {
        //更新缓存
        redisTemplate.boundHashOps(cartKey).put(skuId.toString(), cartInfo);
        //设置过期时间
        setCartKeyExpire(cartKey);
    }

    /**
     * 设置过期时间
     */
    private void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

    /**
     * 获取购物车的key
     */
    private String getCartKey(String userId) {
        //定义key user:userId:cart
        return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
    }

    /**
     * 展示购物车
     * 先从缓存查，缓存没有再去数据库查
     */
    public List<CartInfo> getCartInfo(String userId, String userTempId) {
        //声明一个返回的结果
        List<CartInfo> cartInfoList = new ArrayList<>();
        //未登录
        if (StringUtils.isEmpty(userId)) {
            cartInfoList = getCartInfoList(userTempId);
            return cartInfoList;
        }
        //已经登陆
        /**
         * 合并购物车
         * 1.获取未登录的购物车数据
         * 2.如果未登录购物车有数据，根据条件进行合并
         *      合并条件：skuId 相同 则数量相加，合并完成之后，删除未登录的数据！
         * 3.如果未登录购物车没有数据，则直接显示已经登陆的数据
         */
        if (!StringUtils.isEmpty(userId)) {
            //cartInfoList=getCartInfoList(userId);
            List<CartInfo> tempCartInfoList = getCartInfoList(userTempId);
            //临时购物车不为空，说明需要合并
            if (!CollectionUtils.isEmpty(tempCartInfoList)) {
                //合并购物车
                cartInfoList = mergeCartList(tempCartInfoList, userId);
                //删除未登录购物车数据
                deleteCartList(userTempId);
            }
            //如果未登录的购物车没有数据
            if (StringUtils.isEmpty(userTempId) || CollectionUtils.isEmpty(tempCartInfoList)) {
                cartInfoList = this.getCartInfoList(userId);
            }
        }
        return cartInfoList;
    }

    /**
     * 合并购物车
     *
     * @param tempCartInfoList
     * @param userId
     * @return
     */
    private List<CartInfo> mergeCartList(List<CartInfo> tempCartInfoList, String userId) {
        //获取已经登陆购物车
        List<CartInfo> cartInfoList = getCartInfoList(userId);
        //将已经登陆的购物车转化为map
        Map<Long, CartInfo> cartInfoMap = cartInfoList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));
        //遍历合并
        for (CartInfo cartInfoNoLogin : tempCartInfoList) {
            Long skuId = cartInfoNoLogin.getSkuId();
            //有，更新数量
            if (cartInfoMap.containsKey(skuId)) {
                //更新数量
                CartInfo cartInfo = cartInfoMap.get(skuId);
                cartInfo.setSkuNum(cartInfo.getSkuNum() + cartInfoNoLogin.getSkuNum());
                //更新状态
                cartInfo.setIsChecked(cartInfoNoLogin.getIsChecked());
                //更新数据库
                updateCartInfo(cartInfo);
            } else {
                cartInfoNoLogin.setUserId(userId);
                saveCartInfo(cartInfoNoLogin);
            }
        }
        List<CartInfo> infos = loadCartCache(userId);
        return infos;
    }

    /**
     * 新增一条记录
     *
     * @param cartInfoNoLogin
     */
    @Async
    public void saveCartInfo(CartInfo cartInfoNoLogin) {
        cartInfoMapper.insert(cartInfoNoLogin);
    }

    /**
     * 更新cartInfo
     *
     * @param cartInfo
     */
    @Async
    public void updateCartInfo(CartInfo cartInfo) {
        cartInfoMapper.updateById(cartInfo);
    }

    /**
     * 删除购物车数据
     * 数据库和缓存都要删除
     *
     * @param userTempId
     */
    @Async
    public void deleteCartList(String userTempId) {
        QueryWrapper queryWrapper = new QueryWrapper<CartInfo>();
        queryWrapper.eq("user_id", userTempId);
        cartInfoMapper.delete(queryWrapper);
        String cartKey = getCartKey(userTempId);

        Boolean flag = redisTemplate.hasKey(cartKey);
        if (flag) {
            redisTemplate.delete(cartKey);
        }
    }

    /**
     * 根据用户id获取购物车列表
     * 合并购物车
     *
     * @param userId
     * @return
     */
    public List<CartInfo> getCartInfoList(String userId) {
        //声明一个返回的结果集
        List<CartInfo> cartInfoList = new ArrayList<>();
        //未登录
        if (StringUtils.isEmpty(userId))
            return cartInfoList;
        /**
         * 根据用户id，先查缓存，缓存没有在去查询数据库
         * 定义key：user:userId:cart
         */
        String cartKey = getCartKey(userId);
        cartInfoList = redisTemplate.opsForHash().values(cartKey);
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            //购物车列表显示有顺序，按照商品的更新时间 ，降序
            cartInfoList.sort(Comparator.comparing(o -> o.getId().toString()));
            return cartInfoList;
        } else {
            //缓存没有数据
            if (!redisTemplate.hasKey(cartKey)) {
                cartInfoList = loadCartCache(userId);
            }
            return cartInfoList;
        }
    }

    /**
     * 添加购物车数据到缓存
     *
     * @param userId
     * @return
     */
    private List<CartInfo> loadCartCache(String userId) {

        QueryWrapper queryWrapper = new QueryWrapper<CartInfo>();
        queryWrapper.eq("user_id", userId);
        List<CartInfo> cartInfoList = cartInfoMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(cartInfoList)) {
            return cartInfoList;
        }
        // 将数据库中的数据查询并放入缓存
        Map<String, CartInfo> map = new HashMap<>();
        for (CartInfo cartInfo : cartInfoList) {
            BigDecimal skuPrice = productFeignClient.getSkuPrice(cartInfo.getSkuId());
            cartInfo.setSkuPrice(skuPrice);
            map.put(cartInfo.getSkuId().toString(), cartInfo);
        }
        // 定义key user:userId:cart
        String cartKey = this.getCartKey(userId);
        redisTemplate.opsForHash().putAll(cartKey, map);
        // 设置过期时间
        setCartKeyExpire(cartKey);
        return cartInfoList;
    }

    /**
     * 异步状态变更
     */
    @Async
    public void checkCart(String userId, Integer isChecked, Long skuId) {
        CartInfo cartInfo = new CartInfo();
        cartInfo.setIsChecked(isChecked);
        QueryWrapper queryWrapper = new QueryWrapper<CartInfo>();
        queryWrapper.eq("user_id", userId);
        queryWrapper.eq("sku_id", skuId);
        cartInfoMapper.update(cartInfo, queryWrapper);
    }

    /**
     * 修改商品选中状态业务
     *
     * @param userId
     * @param isChecked
     * @param skuId
     */
    public void updateCheckCart(String userId, Integer isChecked, Long skuId) {
        checkCart(userId, isChecked, skuId);
        //修改缓存
        String cartKey = getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        // 先获取用户选择的商品
        if (hashOperations.hasKey(skuId.toString())) {
            CartInfo cartInfoUpd = hashOperations.get(skuId.toString());
            // cartInfoUpd 写会缓存
            cartInfoUpd.setIsChecked(isChecked);
            // 更新缓存
            hashOperations.put(skuId.toString(), cartInfoUpd);
            // 设置过期时间
            setCartKeyExpire(cartKey);
        }
    }

    /**
     *  异步删除购物车
     */
    @Async
    public void deleteCart(Long skuId, String userId) {
        String cartKey = getCartKey(userId);
        QueryWrapper<CartInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userId).eq("sku_id", skuId);
        cartInfoMapper.delete(wrapper);
        //获取缓存对象
        BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        if (hashOperations.hasKey(skuId.toString())) {
            hashOperations.delete(skuId.toString());
        }
    }
    /**
     * 根据用户id获取选中的购物车列表
     */
    public List<CartInfo> getCartCheckedList(String userId){
        List<CartInfo> cartInfoList=new ArrayList<>();
        String cartKey=getCartKey(userId);
        List<CartInfo> cartCacheInfoList =  redisTemplate.opsForHash().values(cartKey);
        if (!CollectionUtils.isEmpty(cartCacheInfoList) && cartCacheInfoList.size()>0){
            for (CartInfo cartInfo:cartCacheInfoList){
                //获取选中的商品
                if (cartInfo.getIsChecked().intValue()==1){
                    cartInfoList.add(cartInfo);
                }
            }
        }
        return cartInfoList;
    }

}
